#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
统一错误处理系统
提供标准化的异常处理、错误恢复和日志记录功能
"""

import os
import sys
import json
import time
import logging
import traceback
import functools
from typing import Dict, List, Any, Optional, Callable, Union
from dataclasses import dataclass, asdict
from enum import Enum


class ErrorSeverity(Enum):
    """错误严重程度"""
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


class ErrorCategory(Enum):
    """错误类别"""
    SYSTEM = "system"
    NETWORK = "network"
    PERMISSION = "permission"
    CONFIGURATION = "configuration"
    VALIDATION = "validation"
    EXECUTION = "execution"
    RESOURCE = "resource"
    SECURITY = "security"
    ATTRIBUTE = "attribute"


@dataclass
class ErrorContext:
    """错误上下文信息"""
    error_id: str
    timestamp: float
    severity: ErrorSeverity
    category: ErrorCategory
    message: str
    exception_type: str
    traceback_info: str
    function_name: str
    file_path: str
    line_number: int
    additional_data: Dict[str, Any]
    recovery_attempted: bool = False
    recovery_successful: bool = False
    recovery_actions: List[str] = None

    def __post_init__(self):
        if self.recovery_actions is None:
            self.recovery_actions = []


class ErrorRecoveryStrategy:
    """错误恢复策略"""
    
    def __init__(self, name: str, description: str, action: Callable):
        self.name = name
        self.description = description
        self.action = action
        self.success_count = 0
        self.failure_count = 0
    
    def execute(self, error_context: ErrorContext) -> bool:
        """执行恢复策略"""
        try:
            result = self.action(error_context)
            if result:
                self.success_count += 1
                return True
            else:
                self.failure_count += 1
                return False
        except Exception:
            self.failure_count += 1
            return False
    
    @property
    def success_rate(self) -> float:
        """成功率"""
        total = self.success_count + self.failure_count
        return self.success_count / total if total > 0 else 0.0


class EnhancedErrorHandler:
    """增强的错误处理器"""
    
    def __init__(self, log_dir: str = "logs", enable_recovery: bool = True):
        self.log_dir = log_dir
        self.enable_recovery = enable_recovery
        self.error_history: List[ErrorContext] = []
        self.recovery_strategies: Dict[ErrorCategory, List[ErrorRecoveryStrategy]] = {}
        self.error_counter = 0
        
        # 确保日志目录存在
        os.makedirs(self.log_dir, exist_ok=True)
        
        # 设置日志记录器
        self.logger = self._setup_logger()
        
        # 注册默认恢复策略
        self._register_default_recovery_strategies()
    
    def _setup_logger(self) -> logging.Logger:
        """设置日志记录器"""
        logger = logging.getLogger("error_handler")
        logger.setLevel(logging.DEBUG)
        
        # 避免重复添加处理器
        if not logger.handlers:
            # 文件处理器
            file_handler = logging.FileHandler(
                os.path.join(self.log_dir, "error_handler.log"),
                encoding='utf-8'
            )
            file_handler.setLevel(logging.DEBUG)
            
            # 控制台处理器
            console_handler = logging.StreamHandler()
            console_handler.setLevel(logging.WARNING)
            
            # 格式化器
            formatter = logging.Formatter(
                '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
            )
            file_handler.setFormatter(formatter)
            console_handler.setFormatter(formatter)
            
            logger.addHandler(file_handler)
            logger.addHandler(console_handler)
        
        return logger
    
    def _register_default_recovery_strategies(self):
        """注册默认恢复策略"""
        
        # 权限错误恢复策略
        self.register_recovery_strategy(
            ErrorCategory.PERMISSION,
            ErrorRecoveryStrategy(
                "retry_with_sudo",
                "使用sudo重试操作",
                self._retry_with_sudo
            )
        )
        
        # 网络错误恢复策略
        self.register_recovery_strategy(
            ErrorCategory.NETWORK,
            ErrorRecoveryStrategy(
                "retry_with_backoff",
                "使用指数退避重试",
                self._retry_with_backoff
            )
        )
        
        # 资源错误恢复策略
        self.register_recovery_strategy(
            ErrorCategory.RESOURCE,
            ErrorRecoveryStrategy(
                "cleanup_resources",
                "清理资源后重试",
                self._cleanup_resources
            )
        )
        
        # 配置错误恢复策略
        self.register_recovery_strategy(
            ErrorCategory.CONFIGURATION,
            ErrorRecoveryStrategy(
                "reset_to_default",
                "重置为默认配置",
                self._reset_to_default_config
            )
        )
    
    def register_recovery_strategy(self, category: ErrorCategory, strategy: ErrorRecoveryStrategy):
        """注册恢复策略"""
        if category not in self.recovery_strategies:
            self.recovery_strategies[category] = []
        self.recovery_strategies[category].append(strategy)
    
    def handle_error(self, 
                    exception: Exception, 
                    severity: ErrorSeverity = ErrorSeverity.MEDIUM,
                    category: ErrorCategory = ErrorCategory.EXECUTION,
                    additional_data: Dict[str, Any] = None,
                    attempt_recovery: bool = None) -> ErrorContext:
        """处理错误"""
        
        if additional_data is None:
            additional_data = {}
        
        if attempt_recovery is None:
            attempt_recovery = self.enable_recovery
        
        # 生成错误ID
        self.error_counter += 1
        error_id = f"ERR_{int(time.time())}_{self.error_counter:04d}"
        
        # 获取调用栈信息
        tb = traceback.extract_tb(exception.__traceback__)
        if tb:
            frame = tb[-1]
            function_name = frame.name
            file_path = frame.filename
            line_number = frame.lineno
        else:
            function_name = "unknown"
            file_path = "unknown"
            line_number = 0
        
        # 创建错误上下文
        error_context = ErrorContext(
            error_id=error_id,
            timestamp=time.time(),
            severity=severity,
            category=category,
            message=str(exception),
            exception_type=type(exception).__name__,
            traceback_info=traceback.format_exc(),
            function_name=function_name,
            file_path=file_path,
            line_number=line_number,
            additional_data=additional_data
        )
        
        # 记录错误
        self._log_error(error_context)
        
        # 尝试恢复
        if attempt_recovery and category in self.recovery_strategies:
            self._attempt_recovery(error_context)
        
        # 保存到历史记录
        self.error_history.append(error_context)
        
        # 保存错误报告
        self._save_error_report(error_context)
        
        return error_context
    
    def _log_error(self, error_context: ErrorContext):
        """记录错误日志"""
        log_message = (
            f"[{error_context.error_id}] {error_context.severity.value.upper()} "
            f"({error_context.category.value}): {error_context.message}"
        )
        
        if error_context.severity == ErrorSeverity.CRITICAL:
            self.logger.critical(log_message)
        elif error_context.severity == ErrorSeverity.HIGH:
            self.logger.error(log_message)
        elif error_context.severity == ErrorSeverity.MEDIUM:
            self.logger.warning(log_message)
        else:
            self.logger.info(log_message)
        
        # 记录详细信息到调试日志
        self.logger.debug(f"详细错误信息: {error_context.traceback_info}")
    
    def _attempt_recovery(self, error_context: ErrorContext):
        """尝试错误恢复"""
        error_context.recovery_attempted = True
        
        strategies = self.recovery_strategies.get(error_context.category, [])
        
        for strategy in strategies:
            self.logger.info(f"尝试恢复策略: {strategy.name}")
            error_context.recovery_actions.append(f"尝试: {strategy.name}")
            
            if strategy.execute(error_context):
                error_context.recovery_successful = True
                error_context.recovery_actions.append(f"成功: {strategy.name}")
                self.logger.info(f"恢复成功: {strategy.name}")
                break
            else:
                error_context.recovery_actions.append(f"失败: {strategy.name}")
                self.logger.warning(f"恢复失败: {strategy.name}")
    
    def _save_error_report(self, error_context: ErrorContext):
        """保存错误报告"""
        report_file = os.path.join(
            self.log_dir, 
            f"error_report_{error_context.error_id}.json"
        )
        
        try:
            with open(report_file, 'w', encoding='utf-8') as f:
                json.dump(asdict(error_context), f, indent=2, ensure_ascii=False, default=str)
        except Exception as e:
            self.logger.error(f"保存错误报告失败: {e}")
    
    def _retry_with_sudo(self, error_context: ErrorContext) -> bool:
        """使用sudo重试操作"""
        # 这是一个示例恢复策略，实际实现需要根据具体情况调整
        return False
    
    def _retry_with_backoff(self, error_context: ErrorContext) -> bool:
        """使用指数退避重试"""
        # 这是一个示例恢复策略，实际实现需要根据具体情况调整
        return False
    
    def _cleanup_resources(self, error_context: ErrorContext) -> bool:
        """清理资源后重试"""
        # 这是一个示例恢复策略，实际实现需要根据具体情况调整
        return False
    
    def _reset_to_default_config(self, error_context: ErrorContext) -> bool:
        """重置为默认配置"""
        # 这是一个示例恢复策略，实际实现需要根据具体情况调整
        return False
    
    def get_error_statistics(self) -> Dict[str, Any]:
        """获取错误统计信息"""
        if not self.error_history:
            return {"total_errors": 0}
        
        stats = {
            "total_errors": len(self.error_history),
            "by_severity": {},
            "by_category": {},
            "recovery_rate": 0.0,
            "recent_errors": []
        }
        
        # 按严重程度统计
        for severity in ErrorSeverity:
            count = sum(1 for e in self.error_history if e.severity == severity)
            stats["by_severity"][severity.value] = count
        
        # 按类别统计
        for category in ErrorCategory:
            count = sum(1 for e in self.error_history if e.category == category)
            stats["by_category"][category.value] = count
        
        # 恢复率
        recovery_attempts = sum(1 for e in self.error_history if e.recovery_attempted)
        recovery_successes = sum(1 for e in self.error_history if e.recovery_successful)
        if recovery_attempts > 0:
            stats["recovery_rate"] = recovery_successes / recovery_attempts
        
        # 最近的错误
        recent_errors = sorted(self.error_history, key=lambda x: x.timestamp, reverse=True)[:5]
        stats["recent_errors"] = [
            {
                "error_id": e.error_id,
                "severity": e.severity.value,
                "category": e.category.value,
                "message": e.message,
                "timestamp": e.timestamp
            }
            for e in recent_errors
        ]
        
        return stats
    
    def generate_error_report(self) -> str:
        """生成错误报告"""
        stats = self.get_error_statistics()
        
        report = []
        report.append("# 错误处理报告")
        report.append(f"生成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
        report.append("")
        
        report.append("## 总体统计")
        report.append(f"- 总错误数: {stats['total_errors']}")
        report.append(f"- 恢复成功率: {stats['recovery_rate']:.2%}")
        report.append("")
        
        if stats['by_severity']:
            report.append("## 按严重程度分布")
            for severity, count in stats['by_severity'].items():
                if count > 0:
                    report.append(f"- {severity.upper()}: {count}")
            report.append("")
        
        if stats['by_category']:
            report.append("## 按类别分布")
            for category, count in stats['by_category'].items():
                if count > 0:
                    report.append(f"- {category.upper()}: {count}")
            report.append("")
        
        if stats['recent_errors']:
            report.append("## 最近错误")
            for error in stats['recent_errors']:
                timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(error['timestamp']))
                report.append(f"- [{error['error_id']}] {timestamp} - {error['severity'].upper()}: {error['message']}")
            report.append("")
        
        # 恢复策略统计
        report.append("## 恢复策略效果")
        for category, strategies in self.recovery_strategies.items():
            if strategies:
                report.append(f"### {category.value.upper()}")
                for strategy in strategies:
                    total_attempts = strategy.success_count + strategy.failure_count
                    if total_attempts > 0:
                        success_rate = strategy.success_rate
                        report.append(f"- {strategy.name}: {success_rate:.2%} ({strategy.success_count}/{total_attempts})")
                report.append("")
        
        return "\n".join(report)


def error_handler(severity: ErrorSeverity = ErrorSeverity.MEDIUM,
                 category: ErrorCategory = ErrorCategory.EXECUTION,
                 attempt_recovery: bool = True,
                 reraise: bool = False):
    """错误处理装饰器"""
    
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                # 获取或创建错误处理器实例
                handler = getattr(wrapper, '_error_handler', None)
                if handler is None:
                    handler = EnhancedErrorHandler()
                    wrapper._error_handler = handler
                
                # 处理错误
                error_context = handler.handle_error(
                    exception=e,
                    severity=severity,
                    category=category,
                    additional_data={
                        'function': func.__name__,
                        'args': str(args)[:200],  # 限制长度
                        'kwargs': str(kwargs)[:200]
                    },
                    attempt_recovery=attempt_recovery
                )
                
                if reraise:
                    raise
                
                return None
        
        return wrapper
    return decorator


# 全局错误处理器实例
global_error_handler = EnhancedErrorHandler()


def handle_critical_error(exception: Exception, additional_data: Dict[str, Any] = None):
    """处理关键错误"""
    return global_error_handler.handle_error(
        exception=exception,
        severity=ErrorSeverity.CRITICAL,
        additional_data=additional_data
    )


def handle_system_error(exception: Exception, additional_data: Dict[str, Any] = None):
    """处理系统错误"""
    return global_error_handler.handle_error(
        exception=exception,
        severity=ErrorSeverity.HIGH,
        category=ErrorCategory.SYSTEM,
        additional_data=additional_data
    )


def handle_network_error(exception: Exception, additional_data: Dict[str, Any] = None):
    """处理网络错误"""
    return global_error_handler.handle_error(
        exception=exception,
        severity=ErrorSeverity.MEDIUM,
        category=ErrorCategory.NETWORK,
        additional_data=additional_data
    )


def handle_permission_error(exception: Exception, additional_data: Dict[str, Any] = None):
    """处理权限错误"""
    return global_error_handler.handle_error(
        exception=exception,
        severity=ErrorSeverity.HIGH,
        category=ErrorCategory.PERMISSION,
        additional_data=additional_data
    )


if __name__ == "__main__":
    # 测试错误处理器
    handler = EnhancedErrorHandler()
    
    try:
        raise ValueError("测试错误")
    except Exception as e:
        error_context = handler.handle_error(e, ErrorSeverity.HIGH, ErrorCategory.VALIDATION)
        print(f"处理错误: {error_context.error_id}")
    
    # 生成报告
    print("\n" + handler.generate_error_report())